package config

import (
	"net"
	"os"
	"smart-flow/server/logger"
	"strings"
	"sync"

	"github.com/fsnotify/fsnotify"
	"go.uber.org/zap"

	"github.com/gin-gonic/gin"
	"github.com/spf13/viper"
)

type ServerConfig struct {
	Port   int    `mapstructure:"port"`
	Mode   string `mapstructure:"mode"`
	Passwd string `mapstructure:"passwd"`
}

type RedisConfig struct {
	Ip       string `mapstructure:"ip"`
	Port     int    `mapstructure:"port"`
	Password string `mapstructure:"password"`
	Db       int    `mapstructure:"db"`
	PoolSize int    `mapstructure:"pool_size"`
}

type ConsoleInfo struct {
	Ip    string `mapstructure:"ip"`
	NetIp string `mapstructure:"net_ip"`
	Port  int    `mapstructure:"port"`
}

type ConsoleConfig struct {
	Servers    []ConsoleInfo `mapstructure:"servers"`
	PublicKey  string        `mapstructure:"public_key"`
	PrivateKey string        `mapstructure:"private_key"`
}

type LcConfig struct {
	Url       string `mapstructure:"url"`
	AppKey    string `mapstructure:"app_key"`
	AppSecret string `mapstructure:"app_secret"`
}

type AgentConfig struct {
	Ver string `mapstructure:"ver"`
}

type PusherConfig struct {
	Ver string `mapstructure:"ver"`
}

type LogConfig struct {
	LogPath string `mapstructure:"path"`
}

type DyConfig struct {
	Bar         int64 `mapstructure:"bar"`
	PusherLimit int64 `mapstructure:"pusher_limit"`
	Deflection  int   `mapstructure:"deflection"`
	OffPeakIncr int   `mapstructure:"off_peak_incr"`
}

type CleanConfig struct {
	Align    int `mapstructure:"align"`
	Redetect int `mapstructure:"redetect"`
}

type BlackConfig struct {
	Uids []string `mapstructure:"uids"`
}

type Config struct {
	Server  ServerConfig  `mapstructure:"server"`
	Redis   RedisConfig   `mapstructure:"redis"`
	Console ConsoleConfig `mapstructure:"console"`
	Lc      LcConfig      `mapstructure:"lc"`
	Agent   AgentConfig   `mapstructure:"agent"`
	Pusher  PusherConfig  `mapstructure:"pusher"`
	Log     LogConfig     `mapstructure:"log"`
	Dy      DyConfig      `mapstructure:"dy"`
	Clean   CleanConfig   `mapstructure:"clean"`
}

var (
	configPath string
	config     *Config
	mu         sync.RWMutex
)

func AppConfig() *Config {
	mu.RLock()
	defer mu.RUnlock()
	return config
}

func LoadConfig(path string) {
	defaultConfig()
	configPath = path
	viper.AddConfigPath(configPath)
	viper.SetConfigName("server")
	viper.SetConfigType("yaml")
	err := viper.ReadInConfig()
	if err != nil {
		panic(err.Error())
	}
	err = viper.Unmarshal(&config)
	if err != nil {
		panic(err.Error())
	}
	loadPublicKeyFile(configPath + "/public.pem")
	loadPrivateKeyFile(configPath + "/private.pem")
	validConfig()
	watcher, _ := fsnotify.NewWatcher()
	err = watcher.Add(configPath + "/server.yaml")
	if err != nil {
		panic(err.Error())
	}
	go func() {
		for event := range watcher.Events {
			if event.Op&fsnotify.Write == fsnotify.Write {
				reloadConfig()
			}
		}
	}()
}

func reloadConfig() {
	mu.Lock()
	defer mu.Unlock()
	viper.AddConfigPath(configPath)
	viper.SetConfigName("server")
	viper.SetConfigType("yaml")
	err := viper.ReadInConfig()
	if err != nil {
		logger.Logger.Error("[系统] 更新配置失败", zap.Error(err))
		return
	}
	var dy DyConfig
	err = viper.UnmarshalKey("dy", &dy)
	if err != nil {
		logger.Logger.Error("[系统] 更新配置失败", zap.Error(err))
	}
	config.Dy = dy
	logger.Logger.Info("[系统] 更新配置成功", zap.Any("dy_config", config.Dy))
}

func normalizeLineEndings(content string) string {
	// 先替换 \r\n 为 \n，再替换 \r 为 \n
	return strings.NewReplacer("\r\n", "\n", "\r", "\n").Replace(content)
}

func loadPublicKeyFile(path string) {
	content, err := os.ReadFile(path)
	if err != nil {
		panic(err.Error())
	}
	config.Console.PublicKey = normalizeLineEndings(string(content))
}

func loadPrivateKeyFile(path string) {
	content, err := os.ReadFile(path)
	if err != nil {
		panic(err.Error())
	}
	config.Console.PrivateKey = normalizeLineEndings(string(content))
}

func defaultConfig() {
	viper.SetDefault("server.port", 8080)
	viper.SetDefault("server.mode", "debug")
	viper.SetDefault("server.passwd", "sf")
	viper.SetDefault("redis.ip", "127.0.0.1")
	viper.SetDefault("redis.port", 6379)
	viper.SetDefault("redis.pool_size", 10)
	viper.SetDefault("console.servers", []ConsoleInfo{})
	viper.SetDefault("log.path", "./log")
}

func validConfig() {
	if config.Server.Port <= 0 {
		panic("server port must be greater than 0")
	}
	if config.Server.Mode != gin.DebugMode && config.Server.Mode != gin.TestMode && config.Server.Mode != gin.ReleaseMode {
		panic("server mode must be debug, test or release")
	}
	if net.ParseIP(config.Redis.Ip) == nil {
		panic("redis ip is invalid")
	}
	if config.Redis.Port <= 0 {
		panic("redis port must be greater than 0")
	}
	if config.Redis.Db < 0 || config.Redis.Db > 15 {
		panic("redis db must be greater than 0 and less than 15")
	}
	if config.Redis.PoolSize <= 0 {
		panic("redis pool size must be greater than 0")
	}
	if config.Agent.Ver == "" {
		panic("agent ver is empty")
	}
	if config.Pusher.Ver == "" {
		panic("pusher ver is empty")
	}
	if config.Dy.Bar < 0 || config.Dy.Bar > 100 {
		panic("dy bar must be greater than 0 and less than 100")
	}
	if config.Dy.PusherLimit < 0 {
		panic("dy pusher limit must be greater than 0")
	}
	if config.Dy.Deflection < 0 {
		panic("dy deflection must be greater than 0")
	}
	if config.Dy.OffPeakIncr < 0 {
		panic("dy off peak incr must be greater than 0")
	}
}
